/**
 * Rational.java
 * @author: liuex
 * 2008-3-12 ����05:49:45
 *
 * note: 
 */
package org.lex.math;

/**
 * Immutable
 */
public class Rational extends AbstractRational implements Comparable<Rational> {
	public static final Rational ZERO = new Rational(0L, 1L);
	private long numerator = 0L;
	private long denominator = 1L;

	public static Rational test(double val) {
		boolean negative = false;
		if (val < 0D) {
			negative = true;
			val = -val;
		}
		int a = (int) java.lang.Math.ceil(val);
		val = val - a;
		Rational b = new Rational((int) (Integer.MAX_VALUE * val), Integer.MAX_VALUE);
		b = b.add(new Rational(a));
		if (true == negative)
			b = b.negate();
		return b;
	}

	public Rational(long numerator) {
		this(numerator, 1L);
	}

	public Rational(long numerator, long denominator) {
		this.numerator = numerator;
		this.denominator = denominator;
		this.normalize();
	}

	public Rational(long integer, long numerator, long denominator) {
		this(integer * denominator + numerator, denominator);
	}

	public long numerator() {
		return numerator;
	}

	public long denominator() {
		return denominator;
	}

	public boolean isZero() {
		return 0L == this.numerator;
	}

	public boolean isPositive() {
		return this.numerator > 0L;
	}

	public boolean isNegative() {
		return this.numerator < 0L;
	}

	public Rational abs() {
		if (this.isPositive() || this.isZero())
			return this;
		return this.negate();
	}

	public Rational negate() {
		return new Rational(-this.numerator, this.denominator);
	}

	public Rational reciprocal() {
		return new Rational(this.denominator, this.numerator);
	}

	public Rational add(Rational r) {
		if (r.isZero())
			return this;
		if (this.isZero())
			return r;
		long lcm = org.lex.math.Math.lcm(this.denominator, r.denominator);
		long a = lcm / this.denominator * this.numerator;
		long b = lcm / r.denominator * r.numerator;
		return new Rational(a + b, lcm);
	}

	public Rational subtract(Rational r) {
		return this.add(r.negate());
	}

	public Rational multiply(Rational r) {
		if (this.isZero() || r.isZero())
			return Rational.ZERO;
		return new Rational(this.numerator * r.numerator, this.denominator * r.denominator);
	}

	public Rational divide(Rational r) {
		return this.multiply(r.reciprocal());
	}

	public String toString() {
		StringBuilder buf = new StringBuilder();
		if (1L == this.denominator)
			buf.append(this.denominator);
		else
			buf.append(this.numerator).append('/').append(this.denominator);
		return buf.toString();
	}

	@Override
	public int compareTo(Rational o) {
		if (null == o)
			return -1;
		if (this == o)
			return 0;
		long this2 = this.numerator * o.denominator;
		long o2 = this.denominator * o.numerator;
		return (int) (this2 - o2);
	}

	@Override
	public boolean equals(Object o) {
		if (null == o)
			return false;
		if (this == o)
			return true;
		if (false == o instanceof Rational)
			return false;
		Rational r = (Rational) o;
		return r.numerator == this.numerator && r.denominator == this.denominator;
	}

	@Override
	public double doubleValue() {
		return (double) this.numerator / this.denominator;
	}

	@Override
	public float floatValue() {
		return (float) this.numerator / this.denominator;
	}

	@Override
	public short shortValue() {
		return (short) (this.numerator / this.denominator);
	}

	@Override
	public int intValue() {
		return (int) (this.numerator / this.denominator);
	}

	@Override
	public long longValue() {
		return this.numerator / this.denominator;
	}

	private void normalize() {
		if (0L == this.denominator)
			throw new ArithmeticException("denominator is zero");
		if (0L == this.numerator) {
			// ʹ��"0"ֻ��һ�ֱ�ʾ����������equals/compareTo
			this.denominator = 1L;
		} else {
			boolean negative = false;
			if (this.numerator < 0L) {
				negative = !negative;
				this.numerator = -this.numerator;
			}
			if (this.denominator < 0L) {
				negative = !negative;
				this.denominator = -this.denominator;
			}
			long gcd = org.lex.math.Math.gcd(numerator, denominator);
			if (negative)
				this.numerator = -this.numerator;
			this.numerator /= gcd;
			this.denominator /= gcd;
		}
	}
}
